<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>异步操作和async函数</title>
</head>
<body>
<script type="text/javascript">
    {
        /**
         * 异步：简单说就是一个任务分成两段，先执行第一段，然后转而执行其他任务，等做好了准备，再回过头执行第二段。
         * 同步：连续的执行就叫做同步。由于是连续执行，不能插入其他任务，所以操作系统从硬盘读取文件的这段时间，程序只能干等着。
         * 回调函数：JavaScript语言对异步编程的实现，就是回调函数。即把任务的第二段单独写在一个函数里面，等到重新执行这个任务的时候，就直接调用这个函数(callback)。
         */
        /*读取文件进行处理，是这样写的*/
        /*fs.readFile('/etc/passwd', function (err, data) {
            if (err) throw err;
            console.log(data);
        });*/
        /*上面代码中，readFile函数的第二个参数，就是回调函数，也就是任务的第二段。等到操作系统返回了/etc/passwd这个文件以后，回调函数才会执行*/
    }
    {
        /**Promise*/
        /*回调函数本身并没有问题，它的问题出现在多个回调函数嵌套。假定读取A文件之后，再读取B文件，代码如下*/
        /*fs.readFile(fileA, function (err, data) {
            fs.readFile(fileB, function (err, data) {
                // ...
            });
        });*/

        /*Promise就是为了解决这个问题而提出的。它不是新的语法功能，而是一种新的写法，允许将回调函数的嵌套，改成链式调用。采用Promise，连续读取多个文件，写法如下*/
        /*var readFile = require('fs-readfile-promise');
        readFile(fileA)
            .then(function(data){
                console.log(data.toString());
            })
            .then(function(){
                return readFile(fileB);
            })
            .then(function(data){
                console.log(data.toString());
            })
            .catch(function(err) {
                console.log(err);
            });*/
        /*可以看到，Promise 的写法只是回调函数的改进，使用then方法以后，异步任务的两段执行看得更清楚了，除此以外，并无新意
        * Promise 的最大问题是代码冗余，原来的任务被Promise 包装了一下，不管什么操作，一眼看去都是一堆 then，原来的语义变得很不清楚*/
    }
    {
        /**
         * Generator函数
         * 整个Generator函数就是一个封装的异步任务，或者说是异步任务的容器。异步操作需要暂停的地方，都用yield语句注明
         */
        function* gen(x) {
            return yield x + 2;
        }

        const g = gen(1);
        console.log(g.next()); // {value: 3, done: false}
        console.log(g.next()); // {value: undefined, done: true}

        /*Generator函数可以暂停执行和恢复执行，这是它能封装异步任务的根本原因
        * 它还有两个特性，使它可以作为异步编程的完整解决方案：函数体内外的数据交换和错误处理机制
        * next方法返回值的value属性，是Generator函数向外输出数据；
        * next方法还可以接受参数，这是向Generator函数体内输入数据*/

        const g1 = gen(1);
        console.log(g1.next()); // {value: 3, done: false}
        console.log(g1.next(2)); // {value: 2, done: true}
        /*第一个next方法的value属性，返回表达式x + 2的值(3)。
        第二个next方法带有参数2，这个参数可以传入 Generator 函数，作为上个阶段异步任务的返回结果，被函数体内的变量y接收。
        因此，这一步的 value 属性，返回的就是2(变量y的值)*/

        /*Generator 函数内部还可以部署错误处理代码，捕获函数体外抛出的错误*/
        function* gen2(x) {
            const y = yield x + 2;
            try {
            } catch (e) {
                console.log(e);
            }
            return y;
        }

        let g2 = gen2(1);
        console.log(g2.next()); // {value: 3, done: false}
        console.log(g2.throw('出错了')); // Uncaught 出错了
        /*上面代码的最后一行，Generator函数体外，使用指针对象的throw方法抛出的错误，可以被函数体内的try ...catch代码块捕获。
        这意味着，出错的代码与处理错误的代码，实现了时间和空间上的分离，这对于异步编程无疑是很重要的*/

        /*使用 Generator 函数，执行一个真实的异步任务*/
        /*const fetch = require('node-fetch');

        function* gen(){
            const url = 'https://api.github.com/users/github';
            const result = yield fetch(url);
            console.log(result.bio);
        }*/


    }
</script>
</body>
</html>